using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Design;
using System.Drawing;
using System.Drawing.Design;
using System.Web.UI;
using System.Web.UI.Design;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace ADEVSO.Web.UI.WebControls
{
	/// <summary>
	/// Extended <see cref="GridView"/>.
	/// </summary>
    public class GridView : System.Web.UI.WebControls.GridView
	{
		#region Constructor
		/// <summary>
		/// Initialises an <see cref="XGridView"/>.
		/// </summary>
        public GridView()
		{
            RowEditing += gridView_RowEditing;
            RowCancelingEdit += gridView_RowCancelingEdit;
		}
		#endregion

		#region Instance members

		#region Events
		/// <summary>
		/// Fires before a row is inserted.
		/// </summary>
		[Category("Action")]
		[Description("Fires before a row is inserted.")]
		public event EventHandler<GridViewInsertEventArgs> RowInserting;

		/// <summary>
		/// Fires after a row has been inserted.
		/// </summary>
		[Category("Action")]
		[Description("Fires after a row has been inserted.")]
		public event EventHandler<GridViewInsertedEventArgs> RowInserted;

		/// <summary>
		/// Raises the <see cref="RowInserting"/> event.
		/// </summary>
		/// <param name="e">Event data.</param>
		protected virtual void OnRowInserting(GridViewInsertEventArgs e)
		{
			if (RowInserting != null)
				RowInserting(this, e);
			//else if (!IsBoundUsingDataSourceID && !e.Cancel)
			//	throw new System.Web.HttpException(string.Format("The GridView '{0}' fired event RowInserting which wasn't handled.", ID));
		}

		/// <summary>
		/// Raises the <see cref="RowInserted"/> event.
		/// </summary>
		/// <param name="e">Event data.</param>
		protected virtual void OnRowInserted(GridViewInsertedEventArgs e)
		{
			if (RowInserted != null)
				RowInserted(this, e);
		}

        // Define a new event called RowClick
        /// <summary>
        /// Fires after a row has been inserted.
        /// </summary>
        [Category("Action")]
        [Description("Method to bind grid.")]
        private static readonly object EventBindingMethod = new object();
        public delegate void BindingMethodEventHandler();
        public event BindingMethodEventHandler BindingMethod
        {
            add
            {
                base.Events.AddHandler(GridView.EventBindingMethod, value);
            }
            remove
            {
                base.Events.RemoveHandler(GridView.EventBindingMethod, value);
            }
        }
        
        protected virtual void OnBindingMethod()
        {
            BindingMethodEventHandler handler = base.Events[GridView.EventBindingMethod] as BindingMethodEventHandler;
            if (handler != null)
                handler();
        }
		#endregion

		/// <summary>
		/// Create the pager.
		/// </summary>
		/// <param name="row">The pager row.</param>
		/// <param name="columnSpan">The number of columns the pager should span.</param>
		/// <param name="pagedDataSource">The data source.</param>
		protected override void InitializePager(GridViewRow row, int columnSpan, PagedDataSource pagedDataSource)
		{
			// Nab total row count from data source
			_dataSourceCount = pagedDataSource.DataSourceCount;

			#region Bonus code - not in article!
			/* Almost as a treat for downloading the code, here's a tip!
			 * 
			 * If you regularly define a PagerTemplate that you're
			 * copy-and-pasting between pages, get rid of it and create the
			 * controls for your pager in this method.  Voila, you get
			 * your customised pager every time with no copy-and-paste!
			 * 
			 * As an example, this pager used the WebDings font to show
			 * video player-like controls for moving between pages.  The
			 * key is to set the CommandName and CommandArgument properties
			 * of your button controls.  Enjoy! */

			// Create table cell for pager
			TableCell cell = new TableCell();
			cell.ColumnSpan = columnSpan;
			cell.Controls.Clear();
			// Create pager controls
			HtmlGenericControl pageControls = new HtmlGenericControl("div");
			pageControls.Style.Add(HtmlTextWriterStyle.FontFamily, "webdings");
			pageControls.Style.Add(HtmlTextWriterStyle.FontSize, "medium");
			pageControls.Style.Add("float", "right");
			LinkButton lnkFirst = new LinkButton();
			lnkFirst.ID = "lnkFirst";
			lnkFirst.ToolTip = "First page";
			lnkFirst.CausesValidation = false;
			lnkFirst.Text = "9";
			lnkFirst.CommandName = "Page";
			lnkFirst.CommandArgument = "First";
			lnkFirst.Enabled = (PageIndex != 0);
			pageControls.Controls.Add(lnkFirst);
			pageControls.Controls.Add(new LiteralControl("&nbsp;"));
			LinkButton lnkPrevious = new LinkButton();
			lnkPrevious.ID = "lnkPrevious";
			lnkPrevious.ToolTip = "Previous page";
			lnkPrevious.CausesValidation = false;
			lnkPrevious.Text = "3";
			lnkPrevious.CommandName = "Page";
			lnkPrevious.CommandArgument = "Prev";
			lnkPrevious.Enabled = lnkFirst.Enabled;
			pageControls.Controls.Add(lnkPrevious);
			pageControls.Controls.Add(new LiteralControl("&nbsp;"));
			LinkButton lnkNext = new LinkButton();
			lnkNext.ID = "lnkNext";
			lnkNext.ToolTip = "Next page";
			lnkNext.CausesValidation = false;
			lnkNext.Text = "4";
			lnkNext.CommandName = "Page";
			lnkNext.CommandArgument = "Next";
			lnkNext.Enabled = (PageIndex < (PageCount - 1));
			pageControls.Controls.Add(lnkNext);
			pageControls.Controls.Add(new LiteralControl("&nbsp;"));
			LinkButton lnkLast = new LinkButton();
			lnkLast.ID = "lnkLast";
			lnkLast.ToolTip = "Last page";
			lnkLast.CausesValidation = false;
			lnkLast.Text = ":";
			lnkLast.CommandName = "Page";
			lnkLast.CommandArgument = "Last";
			lnkLast.Enabled = lnkNext.Enabled;
			pageControls.Controls.Add(lnkLast);
			// Add pager controls to cell
			cell.Controls.Add(pageControls);
			// Add cell to pager row
			row.Cells.Add(cell);
			#endregion
		}

		/// <summary>
		/// Creates the control's child controls.
		/// </summary>
		protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
		{
            int numRows = base.CreateChildControls(dataSource, dataBinding);
            //no data rows created, create empty table if enabled
            if (numRows == 0 && ShowEmptyTable)
            {
                //create table
                Table table = new Table();
                table.ID = this.ID;

                //create a new header row
                GridViewRow row = base.CreateRow(-1, -1, DataControlRowType.Header, DataControlRowState.Normal);

                //convert the existing columns into an array and initialize
                DataControlField[] fields = new DataControlField[this.Columns.Count];
                this.Columns.CopyTo(fields, 0);
                this.InitializeRow(row, fields);
                table.Rows.Add(row);

                //create the empty row
                row = new GridViewRow(-1, -1, DataControlRowType.DataRow, DataControlRowState.Normal);
                TableCell cell = new TableCell();
                cell.ColumnSpan = this.Columns.Count;
                cell.Width = Unit.Percentage(100);
                cell.Controls.Add(new LiteralControl(EmptyTableRowText));
                cell.Style[HtmlTextWriterStyle.TextAlign] = "center";
                row.Cells.Add(cell);
                row.Style[HtmlTextWriterStyle.TextAlign] = "center";
                table.Rows.Add(row);

                this.Controls.Add(table);
            }
            else
            {
                if (DisplayInsertRow)
                {
                    ICollection cols = CreateColumns(null, false);
                    DataControlField[] fields = new DataControlField[cols.Count];
                    cols.CopyTo(fields, 0);
                    if (Controls.Count == 0)
                    {
                        // Create dummy table for inserting the first entry
                        Table tableControl = new Table();
                        if (ShowHeader)
                        {
                            // Create header
                            _myHeaderRow = CreateRow(-1, -1, DataControlRowType.Header, DataControlRowState.Normal);
                            InitializeRow(_myHeaderRow, fields);
                            // Trigger events
                            GridViewRowEventArgs headerRowArgs = new GridViewRowEventArgs(_myHeaderRow);
                            OnRowCreated(headerRowArgs);
                            tableControl.Rows.Add(_myHeaderRow);
                            if (dataBinding)
                                OnRowDataBound(headerRowArgs);
                        }
                        // Add insert row
                        Controls.Add(tableControl);
                    }
                    else
                        // Use generated header row
                        _myHeaderRow = null;

                    // Create insertion row
                    _insertRow = CreateRow(-1, -1, DataControlRowType.DataRow, InsertRowActive && Page.IsPostBack ? DataControlRowState.Insert : DataControlRowState.Normal);
                    _insertRow.ControlStyle.MergeWith(AlternatingRowStyle);
                    InitializeRow(_insertRow, fields);

                    // Trigger events
                    GridViewRowEventArgs insertRowArgs = new GridViewRowEventArgs(_insertRow);
                    OnRowCreated(insertRowArgs);

                    // Add row to top of table, just below header
                    Controls[0].Controls.AddAt(Controls[0].Controls.IndexOf(HeaderRow) + 1, _insertRow);
                    if (dataBinding)
                        OnRowDataBound(insertRowArgs);
                }
            }
			return numRows;
		}

		/// <summary>
		/// Initialises a row in the grid.
		/// </summary>
		/// <param name="row">The row to initialise.</param>
		/// <param name="fields">The fields with which to initialise the row.</param>
		protected override void InitializeRow(GridViewRow row, DataControlField[] fields)
		{
			base.InitializeRow(row, fields);

			if (row.RowType == DataControlRowType.Header && AscendingImageUrl != null)
			{
				for (int i = 0; i < fields.Length; i++)
				{
					if (SortExpression.Length > 0 && fields[i].SortExpression == SortExpression)
					{
						// Add sort indicator
                        System.Web.UI.WebControls.Image sortIndicator = new System.Web.UI.WebControls.Image();
						sortIndicator.ImageUrl = SortDirection == SortDirection.Ascending ? AscendingImageUrl : DescendingImageUrl;
						sortIndicator.Style.Add(HtmlTextWriterStyle.VerticalAlign, "TextTop");
						row.Cells[i].Controls.Add(sortIndicator);
						break;
					}
				}
			}

			foreach (TableCell cell in row.Cells)
			{
				// If we have an insert button it should only be shown on the insert row
				foreach (Control control in cell.Controls)
				{
					IButtonControl button = control as IButtonControl;
					if (row == _insertRow)
					{
						if (row.RowState == DataControlRowState.Normal)
						{
							// Hide all controls other than the insert button on the insert row
							control.Visible = false;
                            if (button != null && (button.CommandName == "Insert" || button.CommandName == "New"))
                            {
                                control.Visible = true;
                                control.ID = String.Format("CommandField{0}Button", button.CommandName);
                            }
						}
                        else if (button != null && button.CommandName == "Cancel")
                        {
                            // Hide cancel button on insert row
                            //control.Visible = false;
                            control.ID = String.Format("CommandField{0}Button", button.CommandName);
                        }
					}
                    else if (button != null && button.CommandName == "New")
                    {
                        // Hide the insert button from all other rows
                        control.Visible = false;
                        control.ID = String.Format("CommandField{0}Button", button.CommandName);
                    }
				}
			}
		}

		private IOrderedDictionary _insertValues;

		private bool HandleInsertCallback(int affectedRows, Exception ex)
		{
			GridViewInsertedEventArgs e = new GridViewInsertedEventArgs(_insertValues, ex);
			OnRowInserted(e);
			if (ex != null && !e.ExceptionHandled)
				return false;

			RequiresDataBinding = true;
			return true;
		}

        protected void gridView_RowEditing(object sender, GridViewEditEventArgs e)
        {
            EditIndex = e.NewEditIndex;
            OnBindingMethod();
        }

        protected void gridView_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
        {
            EditIndex = -1;
            OnBindingMethod();
        }

		/// <summary>
		/// Raises the <see cref="GridView.RowCommand"/> event.
		/// </summary>
		/// <param name="e">Event data.</param>
		protected override void OnRowCommand(GridViewCommandEventArgs e)
		{
			base.OnRowCommand(e);
            if (e.CommandName == "Cancel")
            {
                if (EditIndex == -1)
                    InsertRowActive = false;
            }
			else if (e.CommandName == "New")
			{
                InsertRowActive = true;
                gridView_RowCancelingEdit(this, new GridViewCancelEditEventArgs(EditIndex));
				RequiresDataBinding = true;
			}
            else if (e.CommandName == "Edit")
            {
                InsertRowActive = false;
            }
            else if (e.CommandName == "Insert")
            {
                // Perform validation if necessary
                bool doInsert = true;
                IButtonControl button = e.CommandSource as IButtonControl;
                if (button != null)
                {
                    if (button.CausesValidation)
                    {
                        Page.Validate(button.ValidationGroup);
                        doInsert = Page.IsValid;
                    }
                }

                if (doInsert)
                {
                    // Get values
                    _insertValues = new OrderedDictionary();
                    ExtractRowValues(_insertValues, _insertRow, true, false);
                    GridViewInsertEventArgs insertArgs = new GridViewInsertEventArgs(_insertRow, _insertValues);
                    OnRowInserting(insertArgs);
                    if (!insertArgs.Cancel && IsBoundUsingDataSourceID)
                    {
                        // Get data source
                        DataSourceView data = GetData();
                        data.Insert(_insertValues, HandleInsertCallback);
                    }
                    InsertRowActive = false;
                }
            }
            else InsertRowActive = false;
		}

		/// <summary>
		/// Renders the contents of the control.
		/// </summary>
		/// <param name="writer">The <see cref="HtmlTextWriter"/> to write to.</param>
		protected override void RenderContents(HtmlTextWriter writer)
		{
			if (ShowResultSummary && PageCount != 0)
			{
				// Create summary controls
				int firstResultIndex = PageIndex * PageSize;
				HtmlGenericControl topSummaryControl = new HtmlGenericControl("div");
				topSummaryControl.Style.Add("float", "left");
				topSummaryControl.InnerHtml = string.Format("<b>Records:</b> {0} to {1} of {2}", firstResultIndex + 1, firstResultIndex + Rows.Count, DataSourceCount);
				HtmlGenericControl bottomSummaryControl = new HtmlGenericControl("div");
                bottomSummaryControl.Style.Add("float", "left");
				bottomSummaryControl.InnerHtml = topSummaryControl.InnerHtml;

				if (PageCount == 1)
				{
					// Add summary to table at the top
                    if (PagerSettings.Position == PagerPosition.Top || PagerSettings.Position == PagerPosition.TopAndBottom)
					    Controls[0].Controls.AddAt(0, CreateSummaryRow(topSummaryControl));
					// Add summary to table at the bottom
                    if (PagerSettings.Position == PagerPosition.Bottom || PagerSettings.Position == PagerPosition.TopAndBottom)
					    Controls[0].Controls.Add(CreateSummaryRow(bottomSummaryControl));
				}
				else
				{
					// Add summary control to top pager
					if (TopPagerRow != null && (PagerSettings.Position == PagerPosition.Top || PagerSettings.Position == PagerPosition.TopAndBottom))
						TopPagerRow.Cells[0].Controls.Add(topSummaryControl);
					// Add summary control to bottom pager
                    if (BottomPagerRow != null && (PagerSettings.Position == PagerPosition.Bottom || PagerSettings.Position == PagerPosition.TopAndBottom))
						BottomPagerRow.Cells[0].Controls.Add(bottomSummaryControl);
				}
			}

			base.RenderContents(writer);
		}

		private TableRow CreateSummaryRow(Control summaryControl)
		{
			TableRow summaryRow = new TableRow();
			TableCell summaryCell = new TableCell();
			summaryCell.ColumnSpan = HeaderRow.Cells.Count;
			summaryRow.Cells.Add(summaryCell);
			summaryCell.Controls.Add(summaryControl);
			return summaryRow;
		}

		private bool DisplayInsertRow
		{
			get { return AllowInserting && PageIndex == 0; }
		}
		#endregion

		#region Properties

		#region DataSourceCount
		private int _dataSourceCount;

		/// <summary>
		/// The total number of rows in the data source.
		/// </summary>
		[Browsable(false)]
		public int DataSourceCount
		{
			get
			{
				if (Rows.Count == 0)
					return 0;
				else if (AllowPaging)
					return _dataSourceCount;
				else
					return Rows.Count;
			}
		}
		#endregion

		#region ShowResultSummary
		/// <summary>
		/// Whether the results summary should be shown.
		/// </summary>
		[DefaultValue(false)]
		[Category("Appearance")]
		[Description("Whether the results summary should be shown.")]
		public bool ShowResultSummary
		{
			get
			{
				if (ViewState["ShowResultSummary"] == null)
					return false;
				else
					return (bool)ViewState["ShowResultSummary"];
			}
			set { ViewState["ShowResultSummary"] = value; }
		}
		#endregion

		#region AllowInserting
		/// <summary>
		/// Whether the GridView will allow rows to be inserted.
		/// </summary>
		[DefaultValue(false)]
		[Category("Behavior")]
		[Description("Whether the GridView will allow rows to be inserted.")]
		public bool AllowInserting
		{
			get
			{
				if (ViewState["AllowInserting"] == null)
					return false;
				else
					return (bool)ViewState["AllowInserting"];
			}
			set
			{
				if (AllowInserting != value)
					RequiresDataBinding = true;

				ViewState["AllowInserting"] = value;
			}
		}
		#endregion

		#region InsertRowActive
		/// <summary>
		/// Whether the insert row is active.
		/// </summary>
		[DefaultValue(true)]
		[Category("Appearance")]
		[Description("Whether the insert row is active.")]
		public bool InsertRowActive
		{
			get
			{
				if (ViewState["InsertRowActive"] == null)
					return true;
				return (bool)ViewState["InsertRowActive"];
			}
			set
			{
				if (InsertRowActive != value)
					RequiresDataBinding = true;

				ViewState["InsertRowActive"] = value;
			}
		}
		#endregion

		#region InsertRow
		private GridViewRow _insertRow;

		/// <summary>
		/// The row in the grid that contains the insert controls.
		/// </summary>
		[Browsable(false)]
		public GridViewRow InsertRow
		{
			get { return _insertRow; }
		}
		#endregion

		#region HeaderRow
		private GridViewRow _myHeaderRow;

		/// <summary>
		/// Gets the row in the grid that corresponds to the header.
		/// </summary>
		[Browsable(false)]
		public override GridViewRow HeaderRow
		{
			get
			{
				if (_myHeaderRow != null)
					return _myHeaderRow;
				else
					return base.HeaderRow;
			}
		}
		#endregion

		#region AscendingImageUrl
		/// <summary>
		/// Image that is displayed when <see cref="SortDirection"/> is ascending.
		/// </summary>
		[Editor(typeof(ImageUrlEditor), typeof(UITypeEditor))]
        [Category("Appearance")]
		[Description("Image that is displayed when SortDirection is ascending.")]
		public string AscendingImageUrl
		{
			get { return ViewState["AscendingImageUrl"] as string; }
			set { ViewState["AscendingImageUrl"] = value; }
		}
		#endregion

		#region DescendingImageUrl
		/// <summary>
		/// Image that is displayed when <see cref="SortDirection"/> is descending.
		/// </summary>
		[Editor(typeof(ImageUrlEditor), typeof(UITypeEditor))]
        [Category("Appearance")]
		[Description("Image that is displayed when SortDirection is descending.")]
		public string DescendingImageUrl
		{
			get { return ViewState["DescendingImageUrl"] as string; }
			set { ViewState["DescendingImageUrl"] = value; }
		}
		#endregion

        #region ShowEmptyTable
        /// <summary>
        /// Enable or Disable generating an empty table if no data rows in source
        /// </summary>
        [DefaultValue("false")]
        [Category("Misc")]
        [Description("Enable or disable generating an empty table with headers if no data rows in source")]
        public bool ShowEmptyTable
        {
            get
            {
                object o = ViewState["ShowEmptyTable"];
                return (o != null ? (bool)o : false);
            }
            set
            {
                ViewState["ShowEmptyTable"] = value;
            }
        }
        #endregion

        #region EmptyTableRowText
        /// <summary>
        /// Get or Set Text to display in empty data row
        /// </summary>
        [DefaultValue("")]
        [Category("Misc")]
        [Description("Text to display in empty data row")]
        public string EmptyTableRowText
        {
            get
            {
                object o = ViewState["EmptyTableRowText"];
                return (o != null ? o.ToString() : String.Empty);
            }
            set
            {
                ViewState["EmptyTableRowText"] = value;
            }
        }
        #endregion

		#endregion
	}

	/// <summary>
	/// Provides data for the <see cref="ExtendedGridView.RowInserting"/> event.
	/// </summary>
	public class GridViewInsertEventArgs : CancelEventArgs
	{
		#region Constructor
		/// <summary>
		/// Initialises a <see cref="GridViewInsertEventArgs"/>.
		/// </summary>
		/// <param name="row">The insertion row.</param>
		/// <param name="newValues">The values for the new row.</param>
		public GridViewInsertEventArgs(GridViewRow row, IOrderedDictionary newValues)
		{
			_row = row;
			_values = newValues;
		}
		#endregion

		#region Properties

		#region Row
		private GridViewRow _row;

		/// <summary>
		/// The row containing the input controls.
		/// </summary>
		public GridViewRow Row
		{
			get { return _row; }
		}
		#endregion

		#region NewValues
		private IOrderedDictionary _values;

		/// <summary>
		/// The values for the new row to insert.
		/// </summary>
		public IOrderedDictionary NewValues
		{
			get { return _values; }
		}
		#endregion

		#endregion
	}

	/// <summary>
	/// Provides data for the <see cref="ExtendedGridView.RowInserted"/> delegate.
	/// </summary>
	public class GridViewInsertedEventArgs : EventArgs
	{
		#region Constructor
		/// <summary>
		/// Initialises a <see cref="GridViewInsertedEventArgs"/>.
		/// </summary>
		/// <param name="newValues">The values for the new row.</param>
		/// <param name="ex">The exception that occurred during the insert, if any.</param>
		public GridViewInsertedEventArgs(IOrderedDictionary newValues, Exception ex)
		{
			_values = newValues;
			_exception = ex;
		}
		#endregion

		#region Properties

		#region NewValues
		private IOrderedDictionary _values;

		/// <summary>
		/// The values that were inserted into the new row.
		/// </summary>
		public IOrderedDictionary NewValues
		{
			get { return _values; }
		}
		#endregion

		#region Exception
		private Exception _exception;

		/// <summary>
		/// The exception that occurred during the insert, if any.
		/// </summary>
		public Exception Exception
		{
			get { return _exception; }
		}
		#endregion

		#region ExceptionHandled
		private bool _exceptionHandled;

		/// <summary>
		/// Whether the exception that occurred during the insert has been handled.
		/// </summary>
		public bool ExceptionHandled
		{
			get { return _exceptionHandled; }
			set { _exceptionHandled = value; }
		}
		#endregion

		#endregion
	}
}